//         //update shape sizes
//         for (unsigned i = 0; i < numNeurons; i++) {
//             btCollisionShape* bt = neuronShape[i];
//             float s = neuronSize;
//             float w = s * (1.0 + (fabs(brain->neurons[i]->getOutput())));
//             float h = s * (1.0 + sqrt(fabs(brain->neurons[i]->potential)));
//             bt->setLocalScaling(btVector3(w, h, (w + h) / 2.0));
//         }

// FIXME benchmark NOT checking distance for every bodypart.
// FIXME when picking body, not mouving mouse but moving, bad cam->screendirection

#include "filesystem/be_filesystem.h"
#include "worldb.h"

// Called by collision detection before getting serious, is a chance to say no collision required.
// Used to keep critter's bodyparts from colliding with themselves.
void WorldB::CollisionNearOverride(btBroadphasePair& collisionPair, btCollisionDispatcher& dispatcher, const btDispatcherInfo& dispatchInfo)
{
  btRigidBody* b1 = static_cast<btRigidBody*>(collisionPair.m_pProxy0->m_clientObject);
  btRigidBody* b2 = static_cast<btRigidBody*>(collisionPair.m_pProxy1->m_clientObject);
  Entity* e1 = static_cast<Entity*>(b1->getUserPointer());
  Entity* e2 = static_cast<Entity*>(b2->getUserPointer());
  if(e1 == NULL || e2 == NULL)
  {
    //cerr << "Entity cast error in collide callback." << endl;
    return dispatcher.defaultNearCallback(collisionPair, dispatcher, dispatchInfo);
  }
  else if(e1->type == CRITTER && e2->type == CRITTER)
  {
    if(static_cast<Bodypart*>(e1)->getOwner() == static_cast<Bodypart*>(e2)->getOwner())
    {
      //cerr << "Not colliding." << endl;
      return;
    }

    //cerr << "Colliding." << endl;
  }
  
  dispatcher.defaultNearCallback(collisionPair, dispatcher, dispatchInfo);
}


WorldB::WorldB( Textverbosemessage* textverbosemessage ) :
m_camera(0.73, 1024.0f/768.0f, 0.05f, 5000),
m_textverbosemessage(textverbosemessage)
{
	m_camera.setSceneNode(&m_sceneNodeCamera);

	// settings and pointers
	settings = Settings::Instance();
		critter_maxlifetime = settings->getCVarPtr("critter_maxlifetime");
		critter_maxenergy = settings->getCVarPtr("critter_maxenergy");
		critter_autosaveinterval = settings->getCVarPtr("critter_autosaveinterval");
		critter_autoexchangeinterval = settings->getCVarPtr("critter_autoexchangeinterval");
		critter_killhalfat = settings->getCVarPtr("critter_killhalfat");
		critter_enableomnivores = settings->getCVarPtr("critter_enableomnivores");
		critter_startenergy = settings->getCVarPtr("critter_startenergy");
		critter_retinasize = settings->getCVarPtr("critter_retinasize");

		killhalf_decrenergypct = settings->getCVarPtr("killhalf_decrenergypct");
		killhalf_incrworldsizeX = settings->getCVarPtr("killhalf_incrworldsizeX");
		killhalf_incrworldsizeY = settings->getCVarPtr("killhalf_incrworldsizeY");
		killhalf_decrmaxlifetimepct = settings->getCVarPtr("killhalf_decrmaxlifetimepct");
		
		brain_mutationrate = settings->getCVarPtr("brain_mutationrate");
		body_mutationrate = settings->getCVarPtr("body_mutationrate");

		food_maxlifetime = settings->getCVarPtr("food_maxlifetime");
		food_maxenergy = settings->getCVarPtr("food_maxenergy");
		energy = settings->getCVarPtr("energy");
		headless = settings->getCVarPtr("headless");
		threads = settings->getCVarPtr("threads");
		mincritters = settings->getCVarPtr("mincritters");
		insertcritterevery = settings->getCVarPtr("critter_insertevery");
		worldsizeX = settings->getCVarPtr("worldsizeX");
		worldsizeY = settings->getCVarPtr("worldsizeY");

	statsBuffer = Statsbuffer::Instance();
	critterselection = Critterselection::Instance();
	// home & program directory
	dirlayout = Dirlayout::Instance();

	freeEnergy = *food_maxenergy * *energy;
		
	currentCritterID	= 1;
	insertCritterCounter	= 0;
	autosaveCounter		= 0.0f;
	autoexchangeCounter	= 0.0f;
	insertHight		= 1.0f;
	pause = false;
	mouselook = false;
	mousex = -100;
	mousey = -100;

	
	// NOT THREADED
	m_collisionConfiguration = new btDefaultCollisionConfiguration();
	m_dispatcher = new btCollisionDispatcher(m_collisionConfiguration);
  if(settings->getCVar("selfcollisions") != 1)
    m_dispatcher->setNearCallback(&WorldB::CollisionNearOverride);

	btVector3 worldAabbMin(-10000,-10000,-10000);
	btVector3 worldAabbMax(10000,10000,10000);
	m_broadphase = new btAxisSweep3 (worldAabbMin, worldAabbMax);
	m_broadphase->getOverlappingPairCache()->setInternalGhostPairCallback(new btGhostPairCallback());
	m_solver = new btSequentialImpulseConstraintSolver;
	
	m_dynamicsWorld = new btDiscreteDynamicsWorld(m_dispatcher,m_broadphase,m_solver,m_collisionConfiguration);
	// END NOT THREADED
	
	m_dynamicsWorld->getSolverInfo().m_solverMode = SOLVER_USE_WARMSTARTING | SOLVER_SIMD | SOLVER_ENABLE_FRICTION_DIRECTION_CACHING;
	m_dynamicsWorld->getSolverInfo().m_numIterations = 8;

	// raycast
	raycast = new Raycast(m_dynamicsWorld);

	// mousepicker
	mousepicker = new BeMousePicker(m_dynamicsWorld);

	// reset cam
		resetCamera();

	// threading locks
// 	omp_init_lock(&my_lock1);

}

void WorldB::init()
{
	makeFloor();

	if ( settings->getCVar("autoload") )
		loadAllCritters();
	if ( settings->getCVar("autoloadlastsaved") )
		loadAllLastSavedCritters();
}

void WorldB::castMouseRay()
{
	mouseRay = raycast->cast( m_sceneNodeCamera.getOrigin(), mouseRayTo );
	mouseRayHit = false;
	if ( mouseRay.hit )
	{
			Entity* e = static_cast<Entity*>(mouseRay.hitBody->getUserPointer());
			if ( e )
				if ( e->type == FOOD || e->type == CRITTER )
				{
					mouseRayHit = true;
					mouseRayHitEntity = e;
				}
	}
}
void WorldB::calcMouseDirection()
{
	mouseRayTo = m_camera.getScreenDirection(mousex, mousey);
}

void WorldB::moveInMouseDirection(bool towards)
{
}

void WorldB::unpickBody()
{
	mousepicker->detach();
}

void WorldB::pickBody()
{
	if ( mouseRayHit )
	{
		if ( mouseRayHitEntity->type == FOOD || mouseRayHitEntity->type == CRITTER )
		{
			btRigidBody* b = static_cast<btRigidBody*>(mouseRay.hitBody);

			// if critter, and it's the head's ghostobject we touch, overwrite with head's body
			if ( mouseRayHitEntity->type == CRITTER )
			{
				btCollisionObject* co = static_cast<btCollisionObject*>(mouseRay.hitBody);
				Bodypart* bpart = dynamic_cast<Bodypart*>(mouseRayHitEntity);
        b = bpart->validateHitBody(co, b);
			}
			
      // SJFIXME:  mouspicker shoudln't have/need direct accesss to ispicked.
			mousepicker->attach( b, mouseRay.hitPosition, m_sceneNodeCamera.getOrigin(), mouseRayTo );
      mousepicker->pickedBool = &mouseRayHitEntity->ispicked;
      *mousepicker->pickedBool = true;
		}
	}
}

void WorldB::selectBody()
{
	if ( mouseRayHit )
		if ( mouseRayHitEntity->type == CRITTER )
			critterselection->registerCritter(dynamic_cast<Bodypart*>(mouseRayHitEntity)->getOwner_critter());
}

void WorldB::deselectBody()
{
	if ( mouseRayHit )
		if ( mouseRayHitEntity->type == CRITTER )
			critterselection->unregisterCritter(dynamic_cast<Bodypart*>(mouseRayHitEntity)->getOwner_critter());
}

void WorldB::movePickedBodyTo()
{
	if ( mousepicker->active )
		mousepicker->moveTo( m_sceneNodeCamera.getOrigin(), mouseRayTo );
}

void WorldB::movePickedBodyFrom()
{
	if ( mousepicker->active )
		mousepicker->moveFrom( m_sceneNodeCamera.getOrigin() );
}

void WorldB::process_prephysics(unsigned long long frameCounter)
{
		// kill half?
			killHalf();

		// Remove food
			expireFood();

		// Autoinsert Food
			autoinsertFood();

		// remove all dead critters
			expireCritters();

		// Autosave Critters?
			autosaveCritters();

		// Autoexchange Critters?
			autoexchangeCritters();

		// Autoinsert Critters?
			autoinsertCritters();
}

void WorldB::process_postphysics(unsigned long long frameCounter)
{
// 		omp_set_num_threads( *threads );
		// pre-process all critters
    CritterB* c;
		int lmax = (int)critters.size();
    for(int i=0; i< lmax; i++)
    {
//       omp_set_lock(&my_lock1);
        critters[i]->preprocess(critters, entities, frameCounter);
//       omp_unset_lock(&my_lock1);
    }

		float freeEnergyc = 0.0f;
		for( int i=0; i < lmax; i++)
		{
			c = critters[i];

      // process
      // SJFIXME: Is it helpful, or sloppy, to have critters know the world age?
      // Well, it's helpful NOW, so...
			c->process(critters, entities, frameCounter);

			// record critter used energy
			freeEnergyc += c->energyUsed;

			// procreation if procreation energy trigger is hit
// 			omp_set_lock(&my_lock1);
				procreate(c);
// 			omp_unset_lock(&my_lock1);
		}

    for( int i=0; i < lmax; i++)
    {
      critters[i]->postprocess(critters, entities, frameCounter);
    }

		freeEnergy += freeEnergyc;
		getGeneralStats();
}


void WorldB::process(unsigned long long frameCounter)
{
	if ( !pause )
	{
    process_prephysics(frameCounter);

		// do a bullet step
    m_dynamicsWorld->stepSimulation(0.016667f, 0, 0.016667f);

    process_postphysics(frameCounter);

	}
}

void WorldB::childPositionOffset(btVector3* v)
{
	v->setY(insertHight);
}

void WorldB::procreate( CritterB* c )
{
	if ( c->procreate && c->canProcreate )
	{
		bool brainmutant = false;
		bool bodymutant = false;
		if ( randgen->Instance()->get(1,100) <= *brain_mutationrate )
			brainmutant = true;

		if ( randgen->Instance()->get(1,100) <= *body_mutationrate )
			bodymutant = true;

		btVector3 np = c->body.bodyparts[0]->myMotionState->m_graphicsWorldTrans.getOrigin();

		// position offset
		childPositionOffset(&np);

		CritterB *nc = new CritterB(*c, currentCritterID++, np, brainmutant, bodymutant);

		// display message of birth
			stringstream buf;
			buf << setw(4) << c->critterID << " : " << setw(4) << nc->critterID;
			buf << " ad: " << setw(4) << nc->genotype->adamdist;
			buf << " n: " << setw(4) << nc->brain.totalNeurons << " s: " << setw(5) << nc->brain.totalSynapses;

			if ( brainmutant || bodymutant )
			{
				buf << " ";
				if ( brainmutant ) buf << "brain";
				if ( brainmutant && bodymutant ) buf << "+";
				if ( bodymutant ) buf << "body";
				buf << " mutant";
			}

			m_textverbosemessage->addBirth(buf);
			if (*headless)
				cerr << buf.str()<< endl;

		// split energies in half
			nc->energyLevel = c->energyLevel/2.0f;
			c->energyLevel -= nc->energyLevel;

		// reset procreation energy count
			critters.push_back( nc );
	}
  else if(c->procreate)
  {
    // cerr << "Wants to procreate but cannot." << endl;
  }
}

void WorldB::killHalf()
{
	if ( critters.size() >= *critter_killhalfat )
	{
		killHalfOfCritters();
		
		// reduce energy
		if ( *killhalf_decrenergypct > 0 )
		{
			float dec_amount = ((*food_maxenergy * *energy) - *food_maxenergy) / *food_maxenergy;
			if ( dec_amount >= 0.0f )
			{
				int dec = (dec_amount / 100) * *killhalf_decrenergypct;
				settings->setCVar("energy", *energy-dec );
				//*energy -= dec;
				freeEnergy -= dec * *food_maxenergy;
			}
		}

		// increase worldsizes
		if ( *killhalf_incrworldsizeX > 0 )
		{
			settings->increaseCVar("worldsizeX", *killhalf_incrworldsizeX);
			makeFloor();
		}
		if ( *killhalf_incrworldsizeY > 0 )
		{
			settings->increaseCVar("worldsizeY", *killhalf_incrworldsizeY);
			makeFloor();
		}
		
		// decrease critter_maxlifetime
		if ( *killhalf_decrmaxlifetimepct > 0 )
		{
			int dec = (*critter_maxlifetime / 100) * *killhalf_decrmaxlifetimepct;
			settings->setCVar("critter_maxlifetime", *critter_maxlifetime-dec );
		}
	}
}

void WorldB::autoinsertCritters()
{
	// insert critter if < minimum
	if ( critters.size() < *mincritters )
		insertCritter();

	// insert critter if insertcritterevery is reached
	if ( *insertcritterevery > 0 )
	{
		if ( insertCritterCounter >= *insertcritterevery )
		{
			insertCritter();
			insertCritterCounter = 0;
		}
		else
		{
			insertCritterCounter++;
		}
	}
}

void WorldB::autosaveCritters()
{
	if ( *critter_autosaveinterval > 0 )
	{
		autosaveCounter += ((float)Timer::Instance()->elapsed/1000);
		if ( autosaveCounter > *critter_autosaveinterval )
		{
			autosaveCounter = 0.0f;
			saveAllCritters();
		}
	}
}

void WorldB::autoexchangeCritters()
{
	if ( *critter_autoexchangeinterval > 0 )
	{
		autoexchangeCounter += ((float)Timer::Instance()->elapsed/1000);
		if ( autoexchangeCounter > *critter_autoexchangeinterval )
		{
			autoexchangeCounter = 0.0f;

			// determine exchange directory
			stringstream buf;
			buf << dirlayout->savedir << "/exchange";
			string exchangedir = buf.str();
			
			// save or load? :)
			unsigned int mode = randgen->Instance()->get( 0, 1 );
			
			// always load if critters == 0
			if ( critters.size() == 0 || mode )
			{
				vector<string> files;
				dirH.listContentsFull(exchangedir, files);

				// FIXME same routine as load all critters
				for ( unsigned int i = 0; i < files.size(); i++ )
				{
					if ( parseH.endMatches( ".cr", files[i] ) )
					{
						stringstream buf;
						
						buf << "loading " << files[i];
						if ( !*headless )
						{
							Logbuffer::Instance()->add(buf);
						}

						{
							BeFile befileCritter;
							if ( BeFilesystem::Instance()->load( befileCritter, files[i] ) )
							{
								std::string content;
								std::string line;
								while ( befileCritter.getLine(line) )
								{
									content.append(line);
									content.append("\n");
								}
		// 						fileH.open( files[i], content ); 
								
								CritterB *c = new CritterB(content, m_dynamicsWorld, findPosition());

								if ( !c->loadError)
								{
									critters.push_back( c );

									c->critterID = currentCritterID++;

									// start energy
									freeEnergy -= c->energyLevel;
								}
								else
									delete c;
							}
						}

						BeFilesystem::Instance()->rm( files[i] );
					}
				}
				stringstream buf;
				buf << "Loaded critters from " << exchangedir;
				if ( !*headless )
				{
					Logbuffer::Instance()->add(buf);
				}
				else
					cerr << buf.str()<< endl;
			}
			else
			{
				// pick one to save
				unsigned int savec = randgen->Instance()->get( 0, critters.size()-1 );

				buf << "/" << time(0) << ".cr";
				string filename = buf.str();

				// makde dirs
				if ( !dirH.exists(dirlayout->savedir) )		dirH.make(dirlayout->savedir);
				if ( !dirH.exists(exchangedir) )	dirH.make(exchangedir);

				// save critter
				BeFilesystem::Instance()->save(filename, critters[savec]->genotype->saveGenotype());
// 				fileH.save(filename, critters[savec]->genotype->saveGenotype());

				//cerr << endl << "Saved critters to " << subsavedir << endl << endl;
				stringstream buf2;
				buf2 << "Autoexchange: Saved critter to " << filename;
				if ( !*headless )
					Logbuffer::Instance()->add(buf2);
				else
					cerr << buf2.str()<< endl;
			}
		}
	}
}

void WorldB::autoinsertFood()
{
  unsigned int used = 0;
  for(unsigned int i=0;i<critters.size();i++)
    used += critters[i]->energyLevel;
  for(unsigned int i=0;i<entities.size();i++)
    if(entities[i]->type == FOOD)
      used += static_cast<Bodypart*>(entities[i])->getOwner_food()->energyLevel;

  unsigned int max = *food_maxenergy * *energy; 
	if ( used < max )
	{
		insertRandomFood(1, *food_maxenergy);
	}
}

void WorldB::expireCritters()
{
	for( unsigned int i=0; i < critters.size(); i++)
	{
		// see if energy level isn't below 0 -> die, or die of old age
		if ( critters[i]->energyLevel <= 0.0f )
		{
			stringstream buf;
      buf << setw(4) << critters[i]->critterID << " died of " << critters[i]->deathreason;
			m_textverbosemessage->addDeath(buf);
			if (*headless)
				cerr << buf.str()<< endl;

			removeCritter(i);
			i--;
		}
		// die if y < 200
		else
		{
			btVector3 pos = critters[i]->body.bodyparts[0]->myMotionState->m_graphicsWorldTrans.getOrigin();
			
			if ( pos.getY() < -200.0f )
			{
				stringstream buf;
				buf << setw(4) << critters[i]->critterID << " fell in the pit";
				m_textverbosemessage->addDeath(buf);
				if (*headless)
					cerr << buf.str()<< endl;

				removeCritter(i);
				i--;
			}
		}
	}
}

void WorldB::expireFood()
{
// 	for( unsigned int i=0; i < food.size(); i++)
	for( unsigned int i=0; i < entities.size(); i++)
	{
		if ( entities[i]->type == FOOD )
		{
			Food* f = dynamic_cast<Bodypart*>(entities[i])->getOwner_food();
			// food was eaten
			if ( f->energyLevel <= 0 )
			{
				freeEnergy += f->energyLevel;
				if ( f->isPicked() )
					mousepicker->detach();
				delete f;
				entities.erase(entities.begin()+i);
				i--;
			}
			// old food, this should remove stuff from corners
			else if ( ++f->totalFrames >= *food_maxlifetime )
			{
				freeEnergy += f->energyLevel;
				if ( f->isPicked() )
					mousepicker->detach();
				delete f;
				entities.erase(entities.begin()+i);
				i--;
			}
			// die if y < 200
			else
			{
				btVector3 pos = f->body.bodyparts[0]->myMotionState->m_graphicsWorldTrans.getOrigin();

				if ( pos.getY() < -200.0f )
				{
					freeEnergy += f->energyLevel;
					if ( f->isPicked() )
						mousepicker->detach();
					delete f;
					entities.erase(entities.begin()+i);
					i--;
				}
			}
		}
	}
}

void WorldB::getGeneralStats()
{
	statsBuffer->add( critters, entities );
}

void WorldB::insertRandomFood(int amount, float energy)
{
	for ( int i=0; i < amount; i++ )
	{
		Food *f = new Food(m_dynamicsWorld, findPosition());
    // SJFIXME: why is this set here?
		f->energyLevel = energy;
		entities.push_back( f->body.bodyparts[0] );
	}
}

void WorldB::insertCritter()
{
	CritterB *c = new CritterB(m_dynamicsWorld, currentCritterID++, findPosition());
	critters.push_back( c );

	// start energy
	freeEnergy -= c->energyLevel;
}

btVector3 WorldB::findPosition()
{
	return btVector3( (float)randgen->Instance()->get( 0, 100**worldsizeX ) / 100, insertHight, (float)randgen->Instance()->get( 0, 100**worldsizeY ) / 100 );
}

void WorldB::removeCritter(unsigned int cid)
{
	freeEnergy += critters[cid]->energyLevel;

	if ( critters[cid]->isPicked() )
		mousepicker->detach();
	
	critterselection->unregisterCritterID(critters[cid]->critterID);
	critterselection->deselectCritter(critters[cid]->critterID);

	delete critters[cid];
	critters.erase(critters.begin()+cid);
}

void WorldB::killHalfOfCritters()
{
	for ( unsigned int c = 0; c < critters.size(); c++ )
		removeCritter(c);

/*	// kill oldest
	if ( critters.size() > 0 )
	{
		unsigned int half = (critters.size()+1)/2;
		for ( unsigned int c = 0; c < half; c++ )
			removeCritter(0);
	}*/
}

void WorldB::drawWithGrid()
{
	glClear(GL_STENCIL_BUFFER_BIT);
	glEnable(GL_CULL_FACE);
	glCullFace(GL_BACK);

  drawfloor();
	for( unsigned int i=0; i < critters.size(); i++)
		critters[i]->draw();

	for( unsigned int i=0; i < entities.size(); i++)
	  entities[i]->draw();

	glDisable(GL_STENCIL_TEST);
	glDisable(GL_CULL_FACE);


	#ifdef HAVE_OPENCL
// 	nbody.draw();
	#endif

// 	m_dynamicsWorld->debugDrawWorld();
}

void WorldB::drawfloor()
{
//	for( unsigned int i=0; i < entities.size(); i++)
//		if ( entities[i]->type == WALL )
//			entities[i]->draw();
}

void WorldB::loadAllCritters()
{
	vector<string> files;
	dirH.listContentsFull(dirlayout->loaddir, files);

	for ( unsigned int i = 0; i < files.size(); i++ )
	{
		if ( parseH.endMatches( ".cr", files[i] ) )
		{
			stringstream buf;
			buf << "loading " << files[i];
			Logbuffer::Instance()->add(buf);

			BeFile befileCritter;
			if ( BeFilesystem::Instance()->load( befileCritter, files[i] ) )
			{
				std::string content;
				std::string line;
				while ( befileCritter.getLine(line) )
				{
					content.append(line);
					content.append("\n");
				}
			
				CritterB *c = new CritterB(content, m_dynamicsWorld, findPosition());

				if ( !c->loadError)
				{
					critters.push_back( c );

					c->critterID = currentCritterID++;

					// start energy
					freeEnergy -= c->energyLevel;
				}
				else
					delete c;
			}
		}
	}

	stringstream buf;
	buf << "Loaded critters from " << dirlayout->loaddir;
	Logbuffer::Instance()->add(buf);
	//cerr << endl << "Loaded critters from " << loaddir << endl << endl;
}

void WorldB::loadAllLastSavedCritters() // FIXME overlap with previous function
{
	cerr << "loading" << endl;
	vector<string> files;
	
	string filen = dirlayout->progdir;
	filen.append("/lastsaved");
	
	if ( boost::filesystem::exists(filen) )
	{
		BeFile befile;
		if ( BeFilesystem::Instance()->load( befile, filen ) )
		{
			std::string lastsaveddir;
// 			std::string content;
			std::string line;
			while ( befile.getLine(line) )
			{
				lastsaveddir.append(line);
				lastsaveddir.append("\n");
			}

	// 		fileH.open( filen, lastsaveddir ); 
			
			lastsaveddir = lastsaveddir.substr(0, lastsaveddir.length() - 1);
			
		// 	lastsaveddir.append("/");
		cerr << lastsaveddir << endl;

			dirH.listContentsFull(lastsaveddir, files);

		cerr << "found file: " << files.size() << endl;
			for ( unsigned int i = 0; i < files.size(); i++ )
			{
				if ( parseH.endMatches( ".cr", files[i] ) )
				{
					stringstream buf;
					buf << "loading " << files[i];
					Logbuffer::Instance()->add(buf);

					BeFile befileCritter;
					if ( BeFilesystem::Instance()->load( befileCritter, files[i] ) )
					{
						std::string content;
						std::string line;
						while ( befileCritter.getLine(line) )
						{
							content.append(line);
							content.append("\n");
						}
					
						CritterB *c = new CritterB(content, m_dynamicsWorld, findPosition());

						if ( !c->loadError)
						{
							critters.push_back( c );

							c->critterID = currentCritterID++;

							// start energy
							freeEnergy -= c->energyLevel;
						}
						else
							delete c;
					}
				}
			}
			stringstream buf;
			buf << "Loaded critters from " << lastsaveddir;
			Logbuffer::Instance()->add(buf);
			//cerr << endl << "Loaded critters from " << loaddir << endl << endl;
		}
	}
}

void WorldB::saveAllCritters()
{
	// determine save directory
	stringstream buf;
	buf << dirlayout->savedir << "/" << settings->profileName;
	string subprofiledir = buf.str();
	
	buf << "/" << time(0);
	string subsavedir = buf.str();

	// make dirs
	if ( !dirH.exists(dirlayout->savedir) )	dirH.make(dirlayout->savedir);
	if ( !dirH.exists(subprofiledir) )	dirH.make(subprofiledir);
	if ( !dirH.exists(subsavedir) )		dirH.make(subsavedir);

	for ( unsigned int i = 0; i < critters.size(); i++ )
	{
		// determine filename
		stringstream filename;
		filename << subsavedir << "/" << "critter" << i << ".cr";
	
		// save critters
		BeFilesystem::Instance()->save(filename.str(), critters[i]->genotype->saveGenotype());
// 		fileH.save(filename.str(), critters[i]->genotype->saveGenotype());
	}

	// save lastsaved file
	stringstream lastsaved;
	lastsaved << dirlayout->progdir << "/" << "lastsaved";
	
	BeFilesystem::Instance()->save( lastsaved.str(), subsavedir );
	cerr << "saved " << lastsaved.str() << " with " << subsavedir << endl;

 	//cerr << endl << "Saved critters to " << subsavedir << endl << endl;
	stringstream buf2;
	buf2 << "Saved critters to " << subsavedir;
	Logbuffer::Instance()->add(buf2);

}

void WorldB::resetCamera()
{
	unsigned int biggest = *worldsizeX;
	if ( *worldsizeY > biggest )
		biggest = 1.4f**worldsizeY;

	m_sceneNodeCamera.setOrigin( btVector3( 0.5f**worldsizeX, 1.3f*biggest, 0.5f**worldsizeY) );
// 	camera.position.setRotation(btQuaternion(btVector3(1, 0, 0), btScalar(90)));
	m_sceneNodeCamera.pitch( -SIMD_HALF_PI ); // 1.5707f  (float)*energy/10


// 	camera.position = btVector3( -0.5f**worldsizeX, -1.3f*biggest, -0.5f**worldsizeY);
// 	camera.rotation = Vector3f( 90.0f,  0.0f, 0.0f);
}

// selected critter actions
int WorldB::findSelectedCritterID()
{
	for ( unsigned int i=0; i < critters.size(); i++ )
		if ( critters[i]->critterID == critterselection->selectedCritter->critterID )
			return i;
	return -1;
}

int WorldB::findCritterID(unsigned int cid)
{
	for ( unsigned int i=0; i < critters.size(); i++ )
		if ( critters[i]->critterID == cid )
			return i;
	return -1;
}

void WorldB::removeSelectedCritter()
{
	removeCritter(findSelectedCritterID());
}

void WorldB::removeAllSelectedCritters()
{
	while ( critterselection->clist.size() > 0 )
		removeCritter( findCritterID(critterselection->clist[0]->critterID) );
}

void WorldB::duplicateCritter(unsigned int cid, bool brainmutant, bool bodymutant)
{
	btVector3 np = critters[cid]->body.bodyparts[0]->myMotionState->m_graphicsWorldTrans.getOrigin();

	// position offset
	childPositionOffset(&np);

	CritterB *nc = new CritterB(*critters[cid], currentCritterID++, np, brainmutant, bodymutant);

	// duplicate energy levels
	nc->energyLevel = *critter_startenergy;
	freeEnergy -= nc->energyLevel;

	critters.push_back( nc );
}

void WorldB::duplicateSelectedCritter()
{
	duplicateCritter( findSelectedCritterID(), false, false );
}
void WorldB::spawnBrainMutantSelectedCritter()
{
	duplicateCritter( findSelectedCritterID(), true, false );
}
void WorldB::spawnBodyMutantSelectedCritter()
{
	duplicateCritter( findSelectedCritterID(), false, true );
}
void WorldB::spawnBrainBodyMutantSelectedCritter()
{
	duplicateCritter( findSelectedCritterID(), true, true );
}

void WorldB::feedSelectedCritter()
{
	CritterB* c = critters[findSelectedCritterID()];
	if ( c->energyLevel < *critter_startenergy )
	{
		float max_currentDiff = (float)*critter_startenergy - c->energyLevel;
		c->energyLevel += max_currentDiff;
		freeEnergy -= max_currentDiff;
	}
}

void WorldB::resetageSelectedCritter()
{
	critters[findSelectedCritterID()]->totalFrames = 0;
}

void WorldB::duplicateAllSelectedCritters()
{
	for ( unsigned int i=0; i < critterselection->clist.size(); i ++ )
		duplicateCritter( findCritterID(critterselection->clist[i]->critterID), false, false );
}
void WorldB::spawnBrainMutantAllSelectedCritters()
{
	for ( unsigned int i=0; i < critterselection->clist.size(); i ++ )
		duplicateCritter( findCritterID(critterselection->clist[i]->critterID), true, false );
}
void WorldB::spawnBodyMutantAllSelectedCritters()
{
	for ( unsigned int i=0; i < critterselection->clist.size(); i ++ )
		duplicateCritter( findCritterID(critterselection->clist[i]->critterID), false, true );
}
void WorldB::spawnBrainBodyMutantAllSelectedCritters()
{
	for ( unsigned int i=0; i < critterselection->clist.size(); i ++ )
		duplicateCritter( findCritterID(critterselection->clist[i]->critterID), true, true );
}



void WorldB::makeFloor()
{
	makeDefaultFloor();
}

void WorldB::makeDefaultFloor()
{
// 	for ( unsigned int i=0; i < walls.size(); i++ )
// 		delete walls[i];
// 	walls.clear();
	for ( unsigned int i=0; i < entities.size(); i++ )
	{
		if ( entities[i]->type == WALL )
		{
			delete entities[i];
			entities.erase(entities.begin()+i);
			i--;
		}
	}

	// Wall Constants
		float WallWidth = 0.5f;
		float WallHalfWidth = WallWidth/2.0f;
		float WallHeight = 2.0f;
		float WallHalfHeight = WallHeight/2.0f;

	// Ground Floor
		btVector3 position( *worldsizeX/2.0f, -WallHalfWidth, *worldsizeY/2.0f );
		Wall* w = new Wall( *worldsizeX, WallWidth, *worldsizeY, position, m_dynamicsWorld, BeColor(0.3f,0.2f,0.1f,0.0f));
		entities.push_back(w);
	
	if ( settings->getCVar("worldwalls") )
	{
    BeColor wc;
    wc.r=0.34f;
    wc.g=0.25f;
    wc.b=0.11f;
    wc.a=0.0f;

		// Left Wall
			position = btVector3 ( 0.0f-WallHalfWidth, WallHalfHeight-WallWidth, *worldsizeY/2.0f );
			w = new Wall( WallWidth, WallHeight, *worldsizeY, position, m_dynamicsWorld, wc );
			entities.push_back(w);
		// Right Wall
			position = btVector3 ( *worldsizeX+WallHalfWidth, WallHalfHeight-WallWidth, *worldsizeY/2.0f );
			w = new Wall( WallWidth, WallHeight, *worldsizeY, position, m_dynamicsWorld, wc );
			entities.push_back(w);
		// Top Wall
			position = btVector3 ( *worldsizeX/2.0f, WallHalfHeight-WallWidth, 0.0f-WallHalfWidth );
			w = new Wall( *worldsizeX+(WallWidth*2), WallHeight, WallWidth, position, m_dynamicsWorld, wc );
			entities.push_back(w);
		// Bottom Wall
			position = btVector3 ( *worldsizeX/2.0f, WallHalfHeight-WallWidth, *worldsizeY+WallHalfWidth );
			w = new Wall( *worldsizeX+(WallWidth*2), WallHeight, WallWidth, position, m_dynamicsWorld, wc );
			entities.push_back(w);
	}
}

unsigned int WorldB::findEntityIndex( unsigned int number, entityType et )
{
  
	unsigned int found = 0;
	
	for ( unsigned int currentIndex = 0; currentIndex < entities.size(); currentIndex++ )
	{
		if ( entities[currentIndex]->type == et )
		{
			found ++;
			if ( found == number )
				return currentIndex;
		}
	}

	cerr << "findEntityIndex: number too high" << endl;
	exit(1);

}

void WorldB::togglePause()
{
	pause = !pause;
}

// void WorldB::toggleSleeper()
// {
// 	sleeper.swap();
// }

void WorldB::toggleMouselook()
{
	mouselook = !mouselook;
	if ( mouselook )
	{
#ifdef _WIN32
		SDL_WarpMouse(0,0);
#endif
		SDL_WM_GrabInput(SDL_GRAB_ON);
		SDL_ShowCursor(SDL_DISABLE);
		// clear remaining poll events
		{ SDL_Event e; while (SDL_PollEvent(&e)) {} };
		
		// release picked objects
		mousepicker->detach();
	}
	else
	{
		SDL_ShowCursor(SDL_ENABLE);
		SDL_WM_GrabInput(SDL_GRAB_OFF);
	}
}

WorldB::~WorldB()
{
	for ( unsigned int i=0; i < entities.size(); i++ )	delete entities[i];
	for ( unsigned int i=0; i < critters.size(); i++ )	delete critters[i];

	delete raycast;
	delete mousepicker;
	
	delete m_dynamicsWorld;

	delete m_collisionConfiguration;
	delete m_dispatcher;
	delete m_broadphase;
	delete m_solver;

// 	omp_destroy_lock(&my_lock1);
}
